package com.atguigu.locks.service;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author : leimingchao
 * @date Date : 2021年11月27日 18:57
 */
@Service
public class RedisIncrementService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private ReentrantLock reentrantLock = new ReentrantLock();

    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private RedissonClient redissonClient;

    public void useRedissonForLock() {

        //获取一把锁。只要各个代码用的锁名一样即可
        RLock lock = redissonClient.getLock("lock");
        //一直等待，阻塞住
        // lock.lock();
        //感知别人删锁。发布订阅模式(实时感知)。 lock监听redis，redis一旦删锁，赶紧尝试去加锁



        //加锁带自动解锁
        lock.lock(3,TimeUnit.SECONDS);
        try {
            Jedis jedis = jedisPool.getResource();
            String num = jedis.get("num");
            Integer i = Integer.decode(num);
            i = i + 1;
            jedis.set("num",i.toString());
            jedis.close();
        } finally {
            //解锁
            lock.unlock();
        }
    }


    public void incrementDistributeLock() {
        /**
         * setnx ---> set if not exist:原子操作。判断+保存
         *
         * setnxex(key,value,expire)
         * 分布式锁的核心
         *     1.加锁。占坑一定要是原子的。(判断如果没有，就给redis中保存值)
         *
         *
         * RedisTemplate和Jedis客户端二选一
         */

        //1.加锁
        // String token = UUID.randomUUID().toString();
        // Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", token, 3, TimeUnit.SECONDS);
        // if (lock) {
        //     ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();
        //     String num = stringStringValueOperations.get("num");
        //     if (num != null) {
        //         Integer i = Integer.decode(num);
        //         i = i +1;
        //         stringStringValueOperations.set("num",i.toString());
        //     }
        //     //删除锁
        //     String script = "if redis.call('get',KEYS[1] == ARGV[1]) then return redis.call('del',KEYS[1]) else return 0 end";
        //     DefaultRedisScript redisScript = new DefaultRedisScript(script);
        //     redisTemplate.execute(redisScript,Arrays.asList("lock"),token);
        //
        //     System.out.println("删除锁完成....");
        // } else {
        //     incrementDistributeLock();
        // }
        /**
         * 1.锁的更多考虑
         *      1.自旋
         *          自旋次数
         *          自旋超时
         *      2.锁设置
         *          锁粒度：细，记录级别
         *              1.各自服务各自锁
         *              2.分析好粒度，不用锁住无关数据，一种数据一种锁，一条数据一个锁
         *      3.锁类型：
         *          读写锁
         */
        Jedis jedis = jedisPool.getResource();
        try {
            String token = UUID.randomUUID().toString();
            String lock = jedis.set("lock", token, SetParams.setParams().ex(3).nx());
            if ("OK".equalsIgnoreCase(lock)) {
                //占位成功，OK
                String num = jedis.get("num");
                Integer i = Integer.decode(num);
                i = i + 1;
                jedis.set("num",i.toString());
                //删除锁
                String script = "if redis.call('get',KEYS[1] == ARGV[1]) then return redis.call('del',KEYS[1]) else return 0 end";
                jedis.eval(script,Collections.singletonList("lock"),Collections.singletonList(token));
                System.out.println("删除锁OK。。。。");
            } else {
                try {
                    TimeUnit.SECONDS.sleep(3);
                    incrementDistributeLock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            jedis.close();
        }

    }

    /**
     * 进程内肯定好使，单机跑。分布式肯定不好使
     */
    public void increment() {
        reentrantLock.lock();
        try {
            ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();
            String num = stringStringValueOperations.get("num");
            if (num != null) {
                Integer i = Integer.decode(num);
                i = i +1;
                stringStringValueOperations.set("num",i.toString());
            }
        } finally {
            reentrantLock.unlock();
        }
    }
}
